package main

import (
	"database/sql"
	"errors"
	"fmt"
	"io"
	"os"
	"strings"

	"github.com/stoewer/go-strcase"
	"gorm.io/gorm"
	"zhonghui/console/warehouse/dbx/db_zh_kernel"
)

func main() {
	args := os.Args

	var dbName, tableName string

	if len(args) > 1 {
		dbName = args[1]
	}

	if len(args) > 2 {
		tableName = args[2]
	}

	switch dbName {
	case "zh_kernel":
		gameKernel(tableName)
	}
}

// 各库不需要生成结构体的表名
var gameKernelSaveTable = []string{"_", "console_manager", "console_groups"}

func gameKernel(t string) {
	c := db_zh_kernel.Client()

	tbs := tables(c)

	for _, b := range tbs {
		if t != "" {
			if b != t {
				continue
			}
		} else {
			// 不生成的表
			if stringInArray(b, gameKernelSaveTable) || StringStartInArray(b, gameKernelSaveTable) {
				continue
			}
		}

		lines := table(c, b)
		save("zh_kernel", b, lines)
	}
}

func StringStartInArray(need string, needArr []string) bool {
	for _, v := range needArr {
		if strings.HasPrefix(need, v) {
			return true
		}
	}
	return false
}

type field struct {
	Field      string
	Type       string
	Collation  sql.NullString
	Null       string
	Key        string
	Default    sql.NullString
	Extra      string
	Privileges string
	Comment    string
}

func table(db *gorm.DB, name string) string {
	fields := cols(db, name)

	var lines string

	var ts1, ts2, ts3, ts4 bool

	for _, f := range fields {
		switch f.Field {
		case "create_time":
			ts1 = true
		case "update_time":
			ts2 = true
		case "created_at":
			ts3 = true
		case "updated_at":
			ts4 = true
		default:
			lines += line(&f) + "\n"
		}
	}

	tsData := ts(ts1, ts2, ts3, ts4)
	if tsData != "" {
		lines += tsData + " // 时间字段"
	}

	return lines
}

func save(dbName, tbName, lines string) {
	fmt.Println(tbName)
	// 处理拆表
	var chai bool
	if strings.HasSuffix(tbName, "_0") {
		tbName = strings.Replace(tbName, "_0", "", -1)
		chai = true
	}

	// todo 这里改成 model_
	d := "model_" + dbName
	dirName := fmt.Sprintf("../%s", d)

	filename := fmt.Sprintf("%s/%s.go", dirName, tbName)
	var f *os.File
	var err error

	_, ex := os.Stat(dirName)

	if ex != nil {
		_ = os.Mkdir(dirName, os.ModePerm)
	}

	// 判断文件
	if checkFileIsExist(filename) { // 如果文件存在
		f, err = os.OpenFile(filename, os.O_RDWR|os.O_TRUNC|os.O_CREATE, 0766) // 打开文件
	} else {
		f, err = os.Create(filename) // 创建文件
	}

	if err != nil {
		panic(err)
	}
	defer f.Close()

	// 模板
	tm := tm2
	if strings.Contains(lines, "model_common") {
		tm = tm1
	}

	var comm string
	if chai {
		comm = "    // 此表为拆分表\n"
	}

	tm = strings.Replace(tm, "<pk>", d, -1)
	tm = strings.Replace(tm, "<tb>", strcase.UpperCamelCase(tbName), -1)
	tm = strings.Replace(tm, "<ln>", comm+lines, -1)
	tm = strings.Replace(tm, "<tbl>", tbName, -1)

	if strings.Contains(tm, "time.Time") {
		tm = strings.Replace(tm, "\"fmt\"", "\"fmt\"\n    \"time\"", -1)
	}

	_, err = io.WriteString(f, tm)
	if err != nil {
		panic(err)
	}
}

func checkFileIsExist(filename string) bool {
	var exist = true
	if _, err := os.Stat(filename); os.IsNotExist(err) {
		exist = false
	}
	return exist
}

func ts(ts1, ts2, ts3, ts4 bool) string {
	if ts1 && ts2 {
		return "\n    model_common.CommonCreateUpdateTimestamp"
	}
	if ts1 && !ts2 {
		return "\n    model_common.CommonCreateTimestamp"
	}
	if ts3 && ts4 {
		return "\n    model_common.CommonCreatedUpdated"
	}
	if ts3 && !ts4 {
		return "\n    model_common.CommonCreated"
	}
	if !ts1 && !ts2 && !ts3 && !ts4 {
		return ""
	}

	panic(errors.New("invalid ts"))
}

func line(f *field) string {
	name := strcase.UpperCamelCase(f.Field)
	if strings.HasSuffix(name, "Id") {
		name = name[:len(name)-2] + "ID"
	}
	types := colType(f.Type, f.Null)
	comment := f.Comment
	if comment == "" {
		comment = "暂无注释"
	}

	l := fmt.Sprintf("    %s %s `gorm:\"column:%s\" json:\"%s\"` // %s", name, types, f.Field, f.Field, comment)

	return l
}

func colType(t, nul string) string {
	nuStr := ""
	if nul == "YES" {
		nuStr = "*"
	}

	if strings.HasPrefix(t, "int") {
		return nuStr + intUnsigned(t, "int")
	}
	if strings.HasPrefix(t, "tinyint") {
		return nuStr + intUnsigned(t, "int8")
	}
	if strings.HasPrefix(t, "smallint") {
		return nuStr + intUnsigned(t, "int16")
	}
	if strings.HasPrefix(t, "bigint") {
		return nuStr + intUnsigned(t, "int64")
	}
	if strings.HasPrefix(t, "char") || strings.HasPrefix(t, "varchar") || strings.HasPrefix(t, "text") || strings.HasPrefix(t, "longtext") {
		return nuStr + "string"
	}
	if stringInArray(t, []string{"datetime", "date", "timestamp"}) {
		return nuStr + "time.Time"
	}
	if strings.HasPrefix(t, "varbinary") {
		return "[]byte"
	}
	if strings.HasPrefix(t, "decimal") {
		return nuStr + "float64"
	}
	if strings.HasPrefix(t, "enum") {
		return "string"
	}
	if strings.HasPrefix(t, "double") {
		return "float64"
	}

	panic("invalid type " + t)
}

func intUnsigned(t, name string) string {
	if strings.Contains(t, "unsigned") {
		return "u" + name
	}
	return name
}

func cols(db *gorm.DB, tableName string) []field {
	list := make([]field, 0)

	rows, err := db.Raw("show full fields from " + tableName).Rows()
	if err != nil {
		panic(err)
	}

	defer rows.Close()
	for rows.Next() {
		f := field{}
		if err := rows.Scan(&f.Field, &f.Type, &f.Collation, &f.Null, &f.Key, &f.Default, &f.Extra, &f.Privileges, &f.Comment); err != nil {
			panic(err)
		}

		list = append(list, f)
	}

	return list
}

func stringInArray(need string, needArr []string) bool {
	for _, v := range needArr {
		if need == v {
			return true
		}
	}
	return false
}

func tables(db *gorm.DB) []string {
	list := make([]string, 0)

	rows, err := db.Raw("show tables").Rows()
	if err != nil {
		panic(err)
	}

	defer rows.Close()
	for rows.Next() {
		var name string
		if err := rows.Scan(&name); err != nil {
			panic(err)
		}

		list = append(list, name)
	}

	return list
}

var tm1 = `package <pk>

import (
	"fmt"

	"zhonghui/console/warehouse/modelx/model_common"
)

type <tb> struct {
<ln>
}

func (<tb>) TableName() string {
	return fmt.Sprintf("%s.<tbl>", DbName)
}
`

var tm2 = `package <pk>

import (
	"fmt"
)

type <tb> struct {
<ln>
}

func (<tb>) TableName() string {
	return fmt.Sprintf("%s.<tbl>", DbName)
}
`
